/*
 Автор: Швейкин Евгений Юрьевич
 Имя модуля: mdio


 Функциональное описание:     
    Модуль выполняет управление трансивером по mdio.
    
*/
//--------------------------------------------------------------------------------------------------
// ********************** МАКРОСЫ ******************************************************************
    // Описывает процесс модуля выделения фронтов сигнала. 
    // Так же описывает создание новых сигналов с именем <имя сигнала> + <_rise> (<_fall>, <_edge>)
    `define FRONT(clk, name)                    \
    logic name``_0;                             \
    logic name``_1;                             \
    logic name``_rise;                          \
    logic name``_fall;                          \
    logic name``_edge;                          \
    always_ff @(posedge clk)                    \
    begin                                       \
        name``_0 <= name;                       \
        name``_1 <= name``_0;                   \
        name``_rise <= name``_0 & (~name``_1);  \
        name``_fall <= name``_1 & (~name``_0);  \
        name``_edge <= name``_0 ^ name``_1;     \
    end
    
// *********************** НАЧАЛО МОДУЛЯ ***********************************************************
module mdio
#(
    parameter   CLK_FREQ   =   32'd100_000_000,
    parameter   MDC_FREQ   =   32'd1_000_000
)
(
    input  logic            reset_n,
    input  logic            clk,
    
    input  logic            set,
    input  logic [4:0]      phy_addr,
    input  logic [4:0]      reg_addr,
    input  logic [15:0]     data_wr,
    
    output logic            mdc,
    output logic            mdio,
    output logic            wre
);


//  ********************* КОНСТАНТЫ ****************************************************************
    // максимальное значение счетчика должно быть равно половине периода формируемого сигнала
    localparam int unsigned MDC_CNT_MAX  = CLK_FREQ / MDC_FREQ / 2;
    
//  ********************* ТИПЫ ДАННЫХ **************************************************************
    typedef struct packed
    {
        logic [1:0]     start;
        logic [1:0]     opcode;
        logic [4:0]     phy_addr;
        logic [4:0]     reg_addr;
        logic [1:0]     ta;
        logic [15:0]    data;
    } mdio_msg_t;
    
//  ********************* ПЕРЕМЕННЫЕ ***************************************************************

    mdio_msg_t      msg_latch;      // Защелкнутое сообщение по уровню
    logic [31:0]    msg;            // Передаваемое сообщение. Сдвиговый регистр.    
    logic [7:0]     bit_cnt;        // Счетчик бит передаваемого сообщения
    
    logic [31:0]    mdc_cnt;        // Счетчик для формирования тактовой интерфейса управления
	
    logic [7:0]     data_latch;     // Защелкнутые данные
// ********************* INITIAL *******************************************************************
    initial mdc     = 0;
    initial mdio    = 0;
    initial mdc_cnt = 0;
//  ********************* ФУНКЦИИ ******************************************************************

//  ********************* ASSIGN *******************************************************************


// ********************* ПРОЦЕССЫ ******************************************************************
    `FRONT (clk, mdc)
    `FRONT (clk, set)
    
    // Формируем выходную частоту последовательного интенрфейса
    always_ff @(posedge clk)       
    begin  
        mdc_cnt    <= mdc_cnt + 32'd1;
        if (mdc_cnt >= MDC_CNT_MAX) begin
            mdc_cnt    <= 32'd0;
            mdc        <= ~mdc;
        end
        else;
    end

    // Защелка параметров сообщения
    always_ff @(posedge clk)       
    begin
        if (set) begin
            msg_latch.start     <= 2'b01;       // Старт
            msg_latch.opcode    <= 2'b01;       // Код операции. ЗАпись
            msg_latch.phy_addr  <= phy_addr;    // Адрес трансивера
            msg_latch.reg_addr  <= reg_addr;    // Адрес регистра
            msg_latch.ta        <= 2'b10;       // 
            msg_latch.data      <= data_wr;     // Записываемые данные
        end
        else;
    end
    
    always_ff @(posedge clk)
    begin
        // Получив запрос на отправку сбросим счетчик битов
        // И перенесем защелкнутые параметры в регистр передаваемого сообщения
        if (set_rise) begin
            bit_cnt <= 8'd32;
            msg     <= msg_latch;
        end
        else;

        // Работаем только по заднему фронту тактовой
        if (mdc_fall) begin
            // Если преданы не все биты сообщения
            if (bit_cnt > 0) begin	
                wre     <= 1'b1;
                bit_cnt <= bit_cnt - 8'd1;
                mdio     <= msg[bit_cnt - 1];
            end
            else begin
                mdio  <= 1'b1;
                // Только когда счетчик битов обнулен, опустим сигнал
                wre   <= 1'b0;
            end
        end
    end
        
// ********************* ПОДКЛЮЧАЕМЫЕ МОДУЛИ *******************************************************
   
endmodule 
`undef FRONT